package com.gitee.wsl.doc.prosemirror.state.command

import com.gitee.wsl.doc.prosemirror.model.attr.Attrs
import com.gitee.wsl.doc.prosemirror.model.node.NodeType
import com.gitee.wsl.doc.prosemirror.state.Command
import com.gitee.wsl.doc.prosemirror.state.CommandDispatch
import com.gitee.wsl.doc.prosemirror.state.EditorState
import com.gitee.wsl.doc.prosemirror.transform.ext.setBlockType

class SetBlockType(val nodeType: NodeType,val attrs: Attrs? = null): Command{
    override fun exec(
        state: EditorState,
        dispatch: CommandDispatch?,
    ): Boolean {
        var applicable = false
        state.selection.ranges.forEach {
            if (applicable) return@forEach
            val from = it.from.pos
            val to = it.to.pos
            state.doc.nodesBetween(from = from, to = to, f = BT@{ node, pos, parent, index ->
                if (applicable) return@BT false
                if (!node.isTextblock || node.hasMarkup(nodeType, attrs)) return@BT true
                if (node.type == nodeType) {
                    applicable = true
                } else {
                    val pos = state.doc.resolve(pos)
                    val index = pos.index()
                    applicable = pos.parent.canReplaceWith(index, index + 1, nodeType)
                }
                true
            })
        }
        if (!applicable) return false
        if (dispatch != null) {
            val tr = state.tr
            state.selection.ranges.forEach {
                val from = it.from.pos
                val to = it.to.pos
                tr.setBlockType(from, to, nodeType, attrs)
            }
            dispatch(tr.scrollIntoView())
        }
        return true
    }

}


/// Returns a command that tries to set the selected textblocks to the
/// given node type with the given attributes.
fun setBlockType(nodeType: NodeType, attrs: Attrs? = null): Command = SetBlockType(nodeType,attrs)

/**
 * export function setBlockType(nodeType: NodeType, attrs: Attrs | null = null): Command {
 *   return function(state, dispatch) {
 *     let applicable = false
 *     for (let i = 0; i < state.selection.ranges.length && !applicable; i++) {
 *       let {$from: {pos: from}, $to: {pos: to}} = state.selection.ranges[i]
 *       state.doc.nodesBetween(from, to, (node, pos) => {
 *         if (applicable) return false
 *         if (!node.isTextblock || node.hasMarkup(nodeType, attrs)) return
 *         if (node.type == nodeType) {
 *           applicable = true
 *         } else {
 *           let $pos = state.doc.resolve(pos), index = $pos.index()
 *           applicable = $pos.parent.canReplaceWith(index, index + 1, nodeType)
 *         }
 *       })
 *     }
 *     if (!applicable) return false
 *     if (dispatch) {
 *       let tr = state.tr
 *       for (let i = 0; i < state.selection.ranges.length; i++) {
 *         let {$from: {pos: from}, $to: {pos: to}} = state.selection.ranges[i]
 *         tr.setBlockType(from, to, nodeType, attrs)
 *       }
 *       dispatch(tr.scrollIntoView())
 *     }
 *     return true
 *   }
 * }
 */